<?php

namespace liketp\route;

use Closure;
use liketp\Request;
use liketp\Route;

class RuleGroup extends Rule
{
    protected $rules = [];
    protected $fullName = '';
    protected $miss = null;

    public function __construct(Route $router, $parent = null, string $name = '', $rule = '')
    {
        $this->router = $router;
        $this->parent = $parent;
        $this->rule = $rule;
        $this->name = trim($name, '/');

        $this->setFullName();

        if ($this->parent) {
            $this->domain = $this->parent->getDomain();
            $this->parent->addRuleItem($this);
        }
    }

    public function addRule(string $rule, $route = null, string $method = '*'): RuleItem
    {
        $name = '';
        if (is_string($route)) $name = $route;

        $method = strtolower($method);
        if ($rule == '' || $rule == '/') $rule .= '$';

        $ruleItem = new RuleItem($this->router, $this, $name, $rule, $route, $method);
        $this->addRuleItem($ruleItem, $method);

        return $ruleItem;
    }

    public function miss($route, $method)
    {
        $method = strtolower($method);
        $ruleItem = new RuleItem($this->router, $this, null, '', $route, $method);
        $ruleItem->setMiss();
        $this->miss = $ruleItem;

        return $ruleItem;
    }

    public function lazy(bool $lazy = true)
    {
        if ($lazy == false) {
            $this->parseGroupRule($this->rule);
            $this->rule = null;
        }

        return $this;
    }

    public function parseGroupRule($rule)
    {
        $originGroup = $this->router->getGroup();
        $this->router->setGroup($this);

        if ($rule instanceof Closure) {
            $this->router->app()->invokeFunction($rule);
        }

        $this->router->setGroup($originGroup);
    }

    public function filterRulesByMethod($method)
    {
        $method = strtolower($method);
        $rules = [];
        foreach ($this->rules as $v) {
            if (in_array($v[0], ['*', $method])) $rules[] = $v;
        }
        return $rules;
    }

    public function check(Request $request, string $url, bool $completeMatch = false)
    {
        $fullName = $this->fullName();
        $rule = '/' . ltrim($fullName, '/');
        $url = '/' . ltrim($url, '/');

        $result = false;
        if (strpos($url, $rule) === 0) {
            $rules = $this->filterRulesByMethod($request->method());
            foreach ($rules as $itemData) {
                $item = $itemData[1];
                if ($res = $item->check($request, $url, $completeMatch)) {
                    $result = $res;
                    break;
                }
            }
        }
        if ($result == false && $this->miss) {
            $result = $this->miss->checkRule($request, $url, [], false);
        }
        return $result;
    }

    public function fullName()
    {
        return $this->fullName;
    }

    public function children($index = null)
    {
        if ($index == 'first') $index = 0;
        if ($index == 'last') $index = -1;

        if (is_null($index)) return $this->rules;

        if (is_numeric($index) && $index >= 0) {
            return $this->rules[$index] ?? null;
        }

        if (is_numeric($index) && $index < 0) {
            $index = count($this->rules) + $index;
            return $this->rules[$index] ?? null;
        }
    }


    //------------------------------------------------ protected methods
    protected function addRuleItem(Rule $rule, string $method = '*')
    {
        if (strpos($method, '|')) {
            $rule->method($method);
            $method = '*';
        }
        $this->rules[] = [$method, $rule];

        if ($rule instanceof RuleItem && $method != 'options') {
            $this->rules[] = ['options', $rule->setAutoOptions()];
        }
        return $this;
    }

    protected function setFullName()
    {
        $prefix = '';
        if ($this->parent) {
            $prefix = $this->parent->fullName();
        }
        $this->fullName = $prefix ? $prefix . '/' . $this->name : $this->name;
    }
}
